/*
 * @(#)LineCounter.java    Jul 14, 2010
 *
 * Copyright 2009 Nyber Shanghai, Inc. All rights reserved.
 */

package com.lab.io;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.lab.util.regex.RegexUtils;

/**
 * 统计代码行数.
 * 
 * TODO 
 * 	1. 区分文件的注释格式
 *
 * @author nickevin@gmail.com
 * @version Jul 14, 2010 6:29:05 PM
 */

public class LineCounter {

	private List<File> fileList = new ArrayList<File>();
	private long line, space, impot, comment;

	//private long accessor, method, field;

	public LineCounter(File file) {
		if (file.isDirectory()) {
			processFolder(file, Type.ALL);
		} else {
			processFile(file);
		}

		parse();
	}

	public LineCounter(File file, Type type) {
		if (file.isDirectory()) {
			processFolder(file, type);
		} else {
			throw new IllegalArgumentException("请指定文件夹类型.");
		}

		parse();
	}

	private void parse() {
		for (File file : fileList) {
			parseLine(file);
		}
	}

	public void parseLine(File file) {
		String cursor = "";

		BufferedReader br = null;
		try {
			br = new BufferedReader(new FileReader(file));
			while ((cursor = br.readLine()) != null) {
				if (isSpace(cursor)) {
					space++;
				} else if (isMultiLineComment(cursor)) {
					comment++;
				} else if (isSingleLineComment(cursor)) {
					comment++;
				} else if (isImport(cursor)) {
					impot++;
				} else {
					line++;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	private boolean isImport(String line) {
		return line.matches("^\\s*import.*;");
	}

	/**
	 * 1. 普通单行注释.
	 * 2. 多行注释写成单行.
	 * 
	 */
	private boolean isSingleLineComment(String line) {
		return line.matches("\\s*[/]{2}.*") || isSpecialMultiLineComment(line);
	}

	private boolean isMultiLineComment(String line) {
		return isMultiLineCommentStart(line) || isMultiLineCommentEnd(line);
	}

	private boolean isMultiLineCommentStart(String line) {
		return line.matches("\\s*/\\*.*") || line.matches("\\s*\\*.*");
	}

	private boolean isMultiLineCommentEnd(String cursor) {
		return cursor.matches(".*\\*/");
	}

	private boolean isSpecialMultiLineComment(String line) {
		return line.matches("\\s*/\\*.*\\*/");
	}

	private boolean isSpace(String line) {
		return line.matches("(\r\n)*");
	}

	private void processFolder(File folder, Type type) {
		File[] files = folder.listFiles(new TypeFilter(type));
		for (File file : files) {
			if (file.isDirectory()) {
				processFolder(file, type);
			}

			fileList.add(file);
		}
	}

	private void processFile(File file) {
		boolean contains = contains(file);
		if (!contains) {
			throw new IllegalArgumentException("当前只能处理如下几种文件类型: [" + Type.ALL
					+ "], 此文件暂不能处理. ");
		}

		fileList.add(file);
	}

	private boolean contains(File file) {
		String extension = RegexUtils.getFilenameExtension(file);
		extension = extension == null ? "" : extension;

		Type[] values = Type.values();
		for (int i = 1; i < values.length; i++) {
			if (extension.equals(values[i].toString())) {
				return true;
			}
		}

		return false;
	}

	public long getLine() {
		return line;
	}

	public String statement() {
		StringBuilder sb = new StringBuilder();
		sb.append("总计	: " + (line + comment + impot + space)).append("\r");
		sb.append("代码	: " + line).append("\r");
		sb.append("导入包	: " + impot).append("\r");
		sb.append("注释	: " + comment).append("\r");
		sb.append("空行	: " + space);

		return sb.append("\r").toString();
	}

	public enum Type {
		ALL(".java .js .xml"), JAVA(".java"), JAVASCRIPT(".js"), XML(".xml");

		private String code;

		Type(String code) {
			this.code = code;
		}

		@Override
		public String toString() {
			return String.valueOf(code);
		}
	}

	private class TypeFilter implements FileFilter {

		private Type type;

		private TypeFilter(Type type) {
			this.type = type;
		}

		public boolean accept(File file) {
			if (file.isDirectory()) {
				fileList.addAll(Arrays.asList(file.listFiles(new TypeFilter(
						type))));
			}

			if (type.equals(Type.ALL)) {
				return contains(file);
			}

			return file.getName().endsWith(type.toString());
		}
	}
}
